/*
 * pattern: Interpreter 
 */
package patterns;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static patterns.Functional.filter;

public class ScriptEngineTest {

    public static void main(String[] args)
            throws ScriptException, NoSuchMethodException {
        List<Person> origin = Arrays.asList(
                new Person("Mike", 45),
                new Person("Sara", 21),
                new Person("Elsa", 34));
        List<Person> filtered = filter(
                origin,
                Functional.jsPredicateA("arg.getAge() > 30"));
        System.out.println(origin);
        System.out.println(filtered);
    }
}

class Functional {

    public static Predicate jsPredicateA(final String jsCode) {
        return new Predicate() {

            @Override
            public boolean evaluate(Object elem)
                    throws ScriptException {
                ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
                engine.put("arg", elem);
                return (Boolean) engine.eval(jsCode);
            }
        };
    }

    public static Predicate jsPredicateB(final String jsCode)
            throws ScriptException {
        String function = "function test(arg) {return " + jsCode + "}";
        final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
        engine.eval(function);
        final Invocable invocable = (Invocable) engine;
        return new Predicate() {

            @Override
            public boolean evaluate(Object elem)
                    throws ScriptException, NoSuchMethodException {
                engine.put("arg", elem);
                return (Boolean) invocable.invokeFunction("test", elem);
            }
        };
    }

    public static <T> List<T> filter(List<T> origin, Predicate<T> p)
            throws ScriptException, NoSuchMethodException {
        ArrayList<T> result = new ArrayList<T>(origin.size());
        for (T elem : origin) {
            if (p.evaluate(elem)) {
                result.add(elem);
            }
        }
        return result;
    }
}

interface Predicate<T> {

    public boolean evaluate(T elem)
            throws ScriptException, NoSuchMethodException;
}